#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"

#include "wizchip_conf.h"
#include "bsp_spi.h"


#if (_WIZCHIP_ == W5100S)
#define VER 0x51
#elif (_WIZCHIP_ == W5500)
#define VER 0x04
#endif

static inline void wizchip_select(void)
{
    gpio_put(PIN_CS, 0);
}

static inline void wizchip_deselect(void)
{
    gpio_put(PIN_CS, 1);
}

/* Hardware reset chip */
void wizchip_reset(void)
{
    gpio_set_dir(PIN_RST, GPIO_OUT);

    gpio_put(PIN_RST, 0);
    sleep_ms(1000);

    gpio_put(PIN_RST, 1);
    sleep_ms(1000);

    bi_decl(bi_1pin_with_name(PIN_RST, "W5100S RESET"));
}

/* Read a byte of data */
static uint8_t wizchip_read(void)
{
    uint8_t rx_data = 0;
    uint8_t tx_data = 0xFF;

    spi_read_blocking(SPI_PORT, tx_data, &rx_data, 1);

    return rx_data;
}
/* Writes a byte of data */
static void wizchip_write(uint8_t tx_data)
{
    spi_write_blocking(SPI_PORT, &tx_data, 1);
}
/* The main control (RP2040) spi interface is initialized. Procedure */
static void wizchip_spi_initialize(void)
{
    // this example will use SPI0 at 5MHz
    spi_init(SPI_PORT, 5000 * 1000);

    gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);

    // make the SPI pins available to picotool
    bi_decl(bi_3pins_with_func(PIN_MISO, PIN_MOSI, PIN_SCK, GPIO_FUNC_SPI));

    // chip select is active-low, so we'll initialise it to a driven-high state
    gpio_init(PIN_CS);
    gpio_set_dir(PIN_CS, GPIO_OUT);
    gpio_put(PIN_CS, 1);

    // make the SPI pins available to picotool
    bi_decl(bi_1pin_with_name(PIN_CS, "W5100S CHIP SELECT"));
}

void wizchip_initialize(void)
{
    uint8_t temp;

    /* Example Initialize the spi interface */
    wizchip_spi_initialize();

    /* Deselect the chip: chip select high */
    wizchip_deselect();

    /* CS function register */
    reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);

    /* SPI function register */
    reg_wizchip_spi_cbfunc(wizchip_read, wizchip_write);

    wizchip_reset();

    /* Read version register */
#if (_WIZCHIP_ == W5100S)
    if (getVER() != VER)
    {
        printf("ACCESS ERR : VERSION !=0x51, read value = 0x%02x\n", getVER());

        while (1)
        {
            sleep_ms(100);
        }
    }
#elif (_WIZCHIP_ == W5500)
    if (getVERSIONR() != VER)
    {
        printf("ACCESS ERR : VERSION !=0x04, read value = 0x%02x\n", getVERSIONR());

        while (1)
        {
            sleep_ms(100);
        }
    }
#endif
    /* Check PHY link status, causes PHY to start normally */
#if (_WIZCHIP_ == W5100S)
    if (read_phy_pwdn(PHYCR1)) // power down status.
    {  
        wizphy_setphypmode(PHY_POWER_NORM); // into normal mode, Avoid phy check blocking
    }
#elif (_WIZCHIP_ == W5500)
    if(getPHYCFGR() & PHYCFGR_OPMDC_PDOWN)
    {
        setPHYCFGR((uint8_t)(PHYCFGR_RST) | PHYCFGR_OPMD);
        setPHYCFGR(PHYCFGR_OPMDC_ALLA | PHYCFGR_OPMD | (~PHYCFGR_RST));
    }
#endif

    do
    {
        if (ctlwizchip(CW_GET_PHYLINK, (void *)&temp) == -1)
        {
            printf(" Unknown PHY link status\n");
            return;
        }
        if (temp == PHY_LINK_ON)
        {
            printf("PHY link\n");
        }
        else
            printf("PHY no link\n");
        sleep_ms(1000);
    } while (temp == PHY_LINK_OFF);
    wizchip_sw_reset(); //  Software Reset
}

/* Network */
void network_initialize(wiz_NetInfo net_info)
{
    ctlnetwork(CN_SET_NETINFO, (void *)&net_info);
}

void print_network_information(wiz_NetInfo *g_net_info)
{
    uint8_t tmp_str[8] = {
        0,
    };

    ctlnetwork(CN_GET_NETINFO, g_net_info); // Get chip configuration information
    ctlwizchip(CW_GET_ID, (void *)tmp_str); // Get chip ID

    if (g_net_info->dhcp == NETINFO_DHCP)
    {
        printf("====================================================================================================\n");
        printf(" %s network configuration : DHCP\n\n", (char *)tmp_str);
    }
    else
    {
        printf("====================================================================================================\n");
        printf(" %s network configuration : static\n\n", (char *)tmp_str);
    }

    printf(" MAC         : %02X:%02X:%02X:%02X:%02X:%02X\n", g_net_info->mac[0], g_net_info->mac[1], g_net_info->mac[2], g_net_info->mac[3], g_net_info->mac[4], g_net_info->mac[5]);
    printf(" IP          : %d.%d.%d.%d\n", g_net_info->ip[0], g_net_info->ip[1], g_net_info->ip[2], g_net_info->ip[3]);
    printf(" Subnet Mask : %d.%d.%d.%d\n", g_net_info->sn[0], g_net_info->sn[1], g_net_info->sn[2], g_net_info->sn[3]);
    printf(" Gateway     : %d.%d.%d.%d\n", g_net_info->gw[0], g_net_info->gw[1], g_net_info->gw[2], g_net_info->gw[3]);
    printf(" DNS         : %d.%d.%d.%d\n", g_net_info->dns[0], g_net_info->dns[1], g_net_info->dns[2], g_net_info->dns[3]);
    printf("====================================================================================================\n\n");
}

#if (_WIZCHIP_ == W5100S)
uint16_t read_phy_pwdn(uint8_t PHYPWDN_regadr)
{
    WIZCHIP_WRITE(PHYRAR, PHYPWDN_regadr);
    WIZCHIP_WRITE(PHYACR, PHYACR_READ);
    while (WIZCHIP_READ(PHYACR))
        ; // wait for command complete
    return (uint16_t)WIZCHIP_READ(PHYCR1);
}
#elif (_WIZCHIP_ == W5500)

#endif